// Copyright (c) 2011 Blue Onion Software, All rights reserved
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web.Hosting;
using System.Xml;
using System.Xml.Serialization;

namespace BlueOnionSoftware.Bloget
{
    internal static class ExportBlog
    {
        internal static void Export(Blog blog, XmlTextWriter stream)
        {
            if (blog == null)
            {
                throw new ArgumentException("ExportBlog(blog)");
            }

            if (stream == null)
            {
                throw new ArgumentException("ExportBlog(stream)");
            }

            var blogML = new blogType();

            blogML.title = CreateTitleType(blog.Title);
            blogML.subtitle = CreateTitleType(blog.Description);
            blogML.rooturl = blog.Link;
            blogML.datecreated = DateTime.UtcNow;

            blogML.extendedproperties = CreateExtendedProperties(blog);
            blogML.categories = CreateCategories(blog);
            blogML.posts = CreatePosts(blog);

            var serializer = new XmlSerializer(typeof (blogType));
            serializer.Serialize(stream, blogML);
            stream.Flush();
        }

        private static titleType CreateTitleType(string text)
        {
            var title = new titleType();
            var textArray = new[] {text};
            title.Text = textArray;
            title.type = contentTypes.text;
            return title;
        }

        private static authorType CreateAuthorType(string name, string email)
        {
            var author = new authorType();
            InitializeNodeType(author, "0", name);
            author.email = email;
            return author;
        }

        private static void InitializeNodeType(nodeType node, string id, string title)
        {
            node.id = id;
            node.title = CreateTitleType(title);
            node.approved = true;
            node.datecreated = DateTime.UtcNow;
            node.datecreatedSpecified = false;
            node.datemodified = DateTime.UtcNow;
            node.datemodifiedSpecified = false;
        }

        private static extendedpropertyType[] CreateExtendedProperties(Blog blog)
        {
            var extendedProperties = new List<extendedpropertyType>();
            var cultureInfo = CultureInfo.InvariantCulture;

            extendedProperties.Add(ExtendedProperty("accepted_license", blog.LicenseAgreement.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("language", blog.Language));
            extendedProperties.Add(ExtendedProperty("copyright", blog.Copyright));
            extendedProperties.Add(ExtendedProperty("webmaster", blog.Webmaster));
            extendedProperties.Add(ExtendedProperty("rss_image", blog.RssChannelImage));
            extendedProperties.Add(ExtendedProperty("rss_footer", blog.RssFooter));
            extendedProperties.Add(ExtendedProperty("rss_entries_per_feed", blog.RssEntriesPerFeed.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("posts_per_page", blog.PostsPerPage.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("allow_comments", blog.AllowComments.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("max_comments_per_post", blog.MaximumCommentsPerPost.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("expire_comments", blog.ExpireComments.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("email_comments", blog.EmailComments.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("email_pingbacks", blog.EmailPingbacks.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("email_to", blog.EmailTo));
            extendedProperties.Add(ExtendedProperty("image_folder", blog.ImageFolder));
            extendedProperties.Add(ExtendedProperty("image_url", blog.ImageUrl));
            extendedProperties.Add(ExtendedProperty("enable_megaweblogapi", blog.EnableMetaWeblogApi.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("enable_rsd", blog.EnableRealSimpleDiscovery.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("enable_auto_pingback", blog.EnableAutoPingBack.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("enable_pingback_service", blog.EnablePingBackService.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("expire_pingbacks", blog.ExpirePingBacks.ToString(cultureInfo)));
            extendedProperties.Add(ExtendedProperty("maximum_pingbacks_per_post", blog.MaximumPingsBacksPerPost.ToString(cultureInfo)));

            return extendedProperties.ToArray();
        }

        private static extendedpropertyType ExtendedProperty(string name, string value)
        {
            var extendedProperty = new extendedpropertyType();
            extendedProperty.name = name;
            extendedProperty.value = value;
            return extendedProperty;
        }

        private static categoryType[] CreateCategories(Blog blog)
        {
            var categories = new List<categoryType>();

            foreach (var category in blog.Categories)
            {
                categories.Add(CreateCategory(category.Id, category.Name));
            }

            return categories.ToArray();
        }

        private static categoryType CreateCategory(int id, string name)
        {
            var category = new categoryType();
            InitializeNodeType(category, id.ToString(CultureInfo.InvariantCulture), name);
            category.parentref = "0";
            category.description = name;
            return category;
        }

        private static postType[] CreatePosts(Blog blog)
        {
            var posts = new List<postType>();

            foreach (var post in blog.Posts)
            {
                posts.Add(CreatePost(post, blog.LoadContent(post.Id)));
            }

            return posts.ToArray();
        }

        private static postType CreatePost(Post post, Content content)
        {
            var postML = new postType();
            InitializeNodeType(postML, post.Id.ToString(), post.Title);

            postML.datecreated = post.Created;
            postML.datecreatedSpecified = true;
            postML.approved = !post.Draft;
            postML.posturl = string.Empty;
            postML.type = blogpostTypes.normal;
            postML.typeSpecified = true;
            postML.postname = CreateTitleType(post.Title);

            // Categories
            var categoryRefs = new List<categoryRefType>();

            foreach (var tag in post.Tags)
            {
                var categoryRef = new categoryRefType();
                categoryRef.@ref = tag.ToString(CultureInfo.InvariantCulture);
                categoryRefs.Add(categoryRef);
            }

            postML.categories = categoryRefs.ToArray();

            // Author
            var authorRef = new authorRefType();
            authorRef.@ref = "0";
            var authorsRef = new authorsRefType();
            authorsRef.author = authorRef;
            postML.authors = authorsRef;

            // Text
            var contentML = new contentType();
            contentML.type = contentTypes.text;
            contentML.Value = content.Text;
            postML.content = contentML;

            // Comments
            var comments = new List<commentType>();

            foreach (var comment in content.Comments)
            {
                var commentML = new commentType();
                InitializeNodeType(commentML, comment.Id.ToString(), comment.Title);
                commentML.datecreated = comment.Date;
                commentML.datecreatedSpecified = true;
                commentML.username = comment.Author;
                commentML.useremail = comment.Email;

                var commentText = new contentType();
                commentText.type = contentTypes.text;
                commentText.Value = comment.Text;
                commentML.content = commentText;

                comments.Add(commentML);
            }

            postML.comments = comments.ToArray();

            // Trackbacks
            var trackbacks = new List<trackbackType>();

            foreach (var reference in content.References)
            {
                var trackback = new trackbackType();
                InitializeNodeType(trackback, reference.Id.ToString(), reference.Link);
                trackback.url = reference.Link;
                trackback.datecreated = reference.Date;
                trackback.datecreatedSpecified = true;
                trackbacks.Add(trackback);
            }

            postML.trackbacks = trackbacks.ToArray();

            // Attachments
            var attachments = new List<attachmentType>();

            foreach (var attachment in content.Attachments)
            {
                var attachmentML = new attachmentType();
                attachmentML.embedded = true;
                attachmentML.mimetype = attachment.MimeType;

                using (var stream = FileUtilities.OpenRead(HostingEnvironment.MapPath(attachment.Url)))
                {
                    var bytes = new byte[stream.Length];

                    if (stream.Read(bytes, 0, bytes.Length) != bytes.Length)
                    {
                        throw new InvalidOperationException("Attachment length inconsistent");
                    }

                    attachmentML.Value = Convert.ToBase64String(bytes);
                }
            }

            postML.attachments = attachments.ToArray();
            return postML;
        }
    }
}